﻿using System;
using System.Collections.Generic;
using System.Xml;

namespace DarkAngels.WoW.Library.Armory.Character.tabs
{
    public class CharacterTab : AbstractDataClass
    {
        private const string XPATH_CHARACTER_BARS_NODE =             "/page/characterInfo/characterTab/characterBars";
        private const string XPATH_CHARACTER_BASE_NODE =             "/page/characterInfo/characterTab/baseStats";
        private const string XPATH_CHARACTER_DEFENSE_NODE =          "/page/characterInfo/characterTab/defenses";
        private const string XPATH_CHARACTER_MELEE_NODE =            "/page/characterInfo/characterTab/melee";
        private const string XPATH_CHARACTER_RANGED_NODE =           "/page/characterInfo/characterTab/ranged";
        private const string XPATH_CHARACTER_SPELL_NODE =            "/page/characterInfo/characterTab/spell";
        private const string XPATH_CHARACTER_RESISTANCES_NODE =      "/page/characterInfo/characterTab/resistances";
        private const string XPATH_CHARACTER_PROFESSIONS_LIST =      "/page/characterInfo/characterTab/professions/skill";
        private const string XPATH_CHARACTER_BASIC_INFO_NODE =       "/page/characterInfo/character";
        private const string XPATH_CHARACTER_ITEMS_LIST =            "/page/characterInfo/characterTab/items/item";
        private const string XPATH_CHARACTER_KNOWN_TITLES =          "/page/characterInfo/characterTab/knownTitles/title";
        private const string XPATH_CHARACTER_CURRENT_TITLE =         "/page/characterInfo/characterTab/title";
        private const string XPATH_CHARACTER_PVP_CURRENCY_ARENA =    "/page/characterInfo/characterTab/pvp/arenacurrency";
        private const string XPATH_CHARACTER_PVP_HONOR =             "/page/characterInfo/characterTab/pvp/lifetimehonorablekills";
        private Character m_char = null;
        public CharacterTab(Character character)
        {
            m_char = character;
        }

        #region Properties

        public Character Character
        {
            get
            {
                return m_char;
            }
        }

        #region Titles
        private List<String> m_knownTitles = null;
        public List<String> KnownTitles
        {
            get
            {
                if (m_knownTitles == null)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNodeList knownTitles = xmlDoc.SelectNodes(XPATH_CHARACTER_KNOWN_TITLES);
                    string currentTitle = xmlDoc.SelectSingleNode(XPATH_CHARACTER_CURRENT_TITLE).Attributes["value"].Value;
                    _SetKnownTitles(knownTitles, currentTitle);
                }
                return m_knownTitles;
            }
        }
        private string m_currentTitle = null;
        public String CurrentTitle
        {
            get
            {
                if (m_currentTitle == null)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNodeList knownTitles = xmlDoc.SelectNodes(XPATH_CHARACTER_KNOWN_TITLES);
                    string currentTitle = xmlDoc.SelectSingleNode(XPATH_CHARACTER_CURRENT_TITLE).Attributes["value"].Value;
                    _SetKnownTitles(knownTitles, currentTitle);
                }
                return m_currentTitle;
            }
        }
        #endregion

        #region Professions
        private Skill m_profession1;
        /// <summary>
        /// A characters first primary profession
        /// </summary>
        public Skill Profession1
        {
            get
            {
                if (m_profession1.Name == null && XmlData.Length > 0)
                {
                    //try to get the data from the xml-content
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNodeList professions = xmlDoc.SelectNodes(XPATH_CHARACTER_PROFESSIONS_LIST);
                    _SetProfessions(professions);
                }
                return m_profession1;
            }
        }

        private System.Drawing.Image m_profession1icon = null;
        public System.Drawing.Image Profession1Icon
        {
            get
            {
                if (m_profession1.Name == null && XmlData.Length > 0)
                {
                    //try to get the data from the xml-content
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNodeList professions = xmlDoc.SelectNodes(XPATH_CHARACTER_PROFESSIONS_LIST);
                    _SetProfessions(professions);
                }
                if(m_profession1icon == null)
                    m_profession1icon = Character.Provider.GetImage(BuildUri(String.Format("images/icons/professions/{0}-sm.gif", m_profession1.Name.ToLower())));
                return m_profession1icon;
            }
        }

        private Skill m_profession2;
        /// <summary>
        /// A characters second primary profession
        /// </summary>
        public Skill Profession2
        {
            get
            {
                if (m_profession2.Name == null && XmlData.Length > 0)
                {
                    //try to get the data from the xml-content
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNodeList professions = xmlDoc.SelectNodes(XPATH_CHARACTER_PROFESSIONS_LIST);
                    _SetProfessions(professions);
                }
                return m_profession2;
            }
        }

        private System.Drawing.Image m_profession2icon = null;
        public System.Drawing.Image Profession2Icon
        {
            get
            {
                if (m_profession2.Name == null && XmlData.Length > 0)
                {
                    //try to get the data from the xml-content
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNodeList professions = xmlDoc.SelectNodes(XPATH_CHARACTER_PROFESSIONS_LIST);
                    _SetProfessions(professions);
                }
                if(m_profession2icon == null)
                    m_profession2icon = Character.Provider.GetImage(BuildUri(String.Format("images/icons/professions/{0}-sm.gif", m_profession2.Name.ToLower())));
                return m_profession2icon;
            }
        }
        #endregion

        #region PvP
        private Int32 m_lifetimehonorablekills = 0;
        public Int32 LifetimeHonorableKills
        {
            get
            {
                if (m_lifetimehonorablekills == 0)
                {
                    if (XmlData.Length > 0)
                    {
                        XmlDocument xmlDoc = new XmlDocument();
                        xmlDoc.LoadXml(XmlData);
                        XmlNode node = xmlDoc.SelectSingleNode(XPATH_CHARACTER_PVP_HONOR);
                        if (node != null)
                            m_lifetimehonorablekills = Int32.Parse(node.Attributes["value"].Value);
                        else
                            return -2;
                    }
                    else
                    {
                        return -1;
                    }
                }
                return m_lifetimehonorablekills;
            }
        }
        private Int32 m_arenaCurrency = -1;
        public Int32 ArenaCurrency
        {
            get
            {
                if (m_arenaCurrency == -1)
                {
                    if (XmlData.Length > 0)
                    {
                        XmlDocument xmlDoc = new XmlDocument();
                        xmlDoc.LoadXml(XmlData);
                        XmlNode node = xmlDoc.SelectSingleNode(XPATH_CHARACTER_PVP_CURRENCY_ARENA);
                        if (node != null)
                            m_arenaCurrency = Int32.Parse(node.Attributes["value"].Value);
                        else
                            return -2;
                    }
                    else
                    {
                        return -1;
                    }
                }
                return (m_arenaCurrency == -1 ? 0 : m_arenaCurrency);
            }
        }
        #endregion

        #region Character Bars
        private CharacterBar m_resourceBar = null;
        private HealthBar m_healthBar = null;
        public CharacterBar Resource
        {
            get
            {
                if (m_resourceBar == null && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode node = xmlDoc.SelectSingleNode(XPATH_CHARACTER_BARS_NODE);
                    _SetCharacterBars(node);
                }
                return m_resourceBar;
            }
        }
        public HealthBar Health
        {
            get
            {
                if (m_healthBar == null && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode node = xmlDoc.SelectSingleNode(XPATH_CHARACTER_BARS_NODE);
                    _SetCharacterBars(node);
                }
                return m_healthBar;
            }
        }
        #endregion

        #region Spell Stats
        //from the <bonusDamage>-element
        private int m_arcaneBonus = -1;
        private int m_fireBonus = -1;
        private int m_frostBonus = -1;
        private int m_holyBonus = -1;
        private int m_natureBonus = -1;
        private int m_shadowBonus = -1;
        public Int32 ArcaneBonus
        {
            get
            {
                if (m_arcaneBonus == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_arcaneBonus;
            }
        }
        public Int32 FireBonus
        {
            get
            {
                if (m_fireBonus == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_fireBonus;
            }
        }
        public Int32 FrostBonus
        {
            get
            {
                if (m_frostBonus == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_frostBonus;
            }
        }
        public Int32 HolyBonus
        {
            get
            {
                if (m_holyBonus == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_holyBonus;
            }
        }
        public Int32 NatureBonus
        {
            get
            {
                if (m_natureBonus == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_natureBonus;
            }
        }
        public Int32 ShadowBonus
        {
            get
            {
                if (m_shadowBonus == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_shadowBonus;
            }
        }
        //TODO: Add the <petBonus>-element
        private int m_healingBonus = -1;
        public Int32 HealingBonus
        {
            get
            {
                if (m_healingBonus == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_healingBonus;
            }
        }
        //from the <hitRating>-element
        private float m_increasedSpellHitPercent = -1.0F;
        private int m_penetration = -1;
        private int m_reducedResist = -1;
        private int m_spellHitratingValue = -1;
        public float SpellHitIncrease
        {
            get
            {
                if (m_increasedSpellHitPercent == -1.0F && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_increasedSpellHitPercent;
            }
        }
        public Int32 Penetration
        {
            get
            {
                if (m_penetration == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_penetration;
            }
        }
        public Int32 ReducedResist
        {
            get
            {
                if (m_reducedResist == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_reducedResist;
            }
        }
        public Int32 SpellHitRating
        {
            get
            {
                if (m_spellHitratingValue == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_spellHitratingValue;
            }
        }
        //from the <critChance>-element
        private int m_spellCritRating = -1;
        private float m_arcaneCritPercent = -1F;
        private float m_fireCritPercent = -1F;
        private float m_frostCritPercent = -1F;
        private float m_holyCritPercent = -1F;
        private float m_natureCritPercent = -1F;
        private float m_shadowCritPercent = -1F;
        public Int32 SpellCritRating
        {
            get
            {
                if (m_spellCritRating == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_spellCritRating;
            }
        }
        public float ArcaneCritChance
        {
            get
            {
                if (m_arcaneCritPercent == -1F && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_arcaneCritPercent;
            }
        }
        public float FireCritChance
        {
            get
            {
                if (m_fireCritPercent == -1F && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_fireCritPercent;
            }
        }
        public float FrostCritChance
        {
            get
            {
                if (m_frostCritPercent == -1F && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_frostCritPercent;
            }
        }
        public float HolyCritChance
        {
            get
            {
                if (m_holyCritPercent == -1F && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_holyCritPercent;
            }
        }
        public float NatureCritChance
        {
            get
            {
                if (m_natureCritPercent == -1F && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_natureCritPercent;
            }
        }
        public float ShadowCritChance
        {
            get
            {
                if (m_shadowCritPercent == -1F && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_shadowCritPercent;
            }
        }
        //from the <penetration>-element
        private int m_spellPenetration = -1;
        public Int32 SpellPenetration
        {
            get
            {
                if (m_spellPenetration == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_spellPenetration;
            }
        }
        //from the <manaRegen>-element
        private float m_manaRegenCasting = -1F;
        private float m_manaRegenNotCasting = -1F;
        public float ManaRegenCasting
        {
            get
            {
                if (m_manaRegenCasting == -1F && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_manaRegenCasting;
            }
        }
        public float ManaRegenNotCasting
        {
            get
            {
                if (m_manaRegenNotCasting == -1F && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_manaRegenNotCasting;
            }
        }
        //from the <hasteRating>-element
        private int m_spellHasteRating = -1;
        private float m_spellHastePercent = -1F;
        public Int32 SpellHasteRating
        {
            get
            {
                if (m_spellHasteRating == -1 && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_spellHasteRating;
            }
        }
        public float SpellHaste
        {
            get
            {
                if (m_spellHastePercent == -1F && XmlData.Length > 0)
                {
                    //try to load data from the xml document
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    XmlNode spellStats = xmlDoc.SelectSingleNode(XPATH_CHARACTER_SPELL_NODE);
                    _SetSpellStats(spellStats);
                }
                return m_spellHastePercent;
            }
        }
        #endregion

        #region Resistances
        private FireResistance m_fireResistance = null;
        public FireResistance FireResistance
        {
            get
            {
                if (m_fireResistance == null)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetResistances(xmlDoc);
                }
                return m_fireResistance;
            }
        }
        private FrostResistance m_frostResistance = null;
        public FrostResistance FrostResistance
        {
            get
            {
                if (m_frostResistance == null)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetResistances(xmlDoc);
                }
                return m_frostResistance;
            }
        }
        private ShadowResistance m_shadowResistance = null;
        public ShadowResistance ShadowResistance
        {
            get
            {
                if (m_shadowResistance == null)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetResistances(xmlDoc);
                }
                return m_shadowResistance;
            }
        }
        private NatureResistance m_natureResistance = null;
        public NatureResistance NatureResistance
        {
            get
            {
                if (m_natureResistance == null)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetResistances(xmlDoc);
                }
                return m_natureResistance;
            }
        }
        private ArcaneResistance m_arcaneResistance = null;
        public ArcaneResistance ArcaneResistance
        {
            get
            {
                if (m_arcaneResistance == null)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetResistances(xmlDoc);
                }
                return m_arcaneResistance;
            }
        }
        private HolyResistance m_holyResistance = null;
        public HolyResistance HolyResistance
        {
            get
            {
                if (m_holyResistance == null)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetResistances(xmlDoc);
                }
                return m_holyResistance;
            }
        }
        #endregion

        #region Defensive Stats
        // <armor />
        private int m_armor_base = -1;
        public Int32 BaseArmor
        {
            get
            {
                if (m_armor_base < 0 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_armor_base;
            }
        }
        private int m_armor_effective = -1;
        public Int32 EffectiveArmor
        {
            get
            {
                if (m_armor_effective < 0 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_armor_effective;
            }
        }
        private float m_armor_percent = -1.0F;
        public float ArmorReduction
        {
            get
            {
                if (m_armor_percent < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_armor_percent;
            }
        }
        private int m_armor_pet = -1;
        public Int32 PetArmor
        {
            get
            {
                if (m_armor_pet < 0 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_armor_pet;
            }
        }

        // <defense />
        private float m_defense_decrease_percent = -1.0F;
        public float CriticalHitReduction
        {
            get
            {
                if (m_defense_decrease_percent < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_defense_decrease_percent;
            }
        }
        private float m_defense_increase_percent = -1.0F;
        public float DodgeParryIncrease
        {
            get
            {
                if (m_defense_increase_percent < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_defense_increase_percent;
            }
        }
        private int m_defense_additional_defense = -1;
        public Int32 DefenseFromGear
        {
            get
            {
                if (m_defense_additional_defense < 0 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_defense_additional_defense;
            }
        }
        private int m_defense_rating = -1;
        public Int32 DefenseRating
        {
            get
            {
                if (m_defense_rating < 0 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_defense_rating;
            }
        }
        private float m_defense_value = -1.0F;
        public float BaseDefense
        {
            get
            {
                if (m_defense_value < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_defense_value;
            }
        }

        // <dodge />
        private float m_dodge_increase_percent = -1.0F;
        public float DodgeIncreasePercent
        {
            get
            {
                if (m_dodge_increase_percent < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_dodge_increase_percent;
            }
        }
        private float m_dodge_percent = -1.0F;
        public float DodgePercent
        {
            get
            {
                if (m_dodge_percent < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_dodge_percent;
            }
        }
        private int m_dodge_rating = -1;
        public Int32 DodgeRating
        {
            get
            {
                if (m_dodge_rating < 0 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_dodge_rating;
            }
        }

        // <parry />
        private float m_parry_increase_percent = -1.0F;
        public float ParryIncreasePercent
        {
            get
            {
                if (m_parry_increase_percent < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_parry_increase_percent;
            }
        }
        private float m_parry_percent = -1.0F;
        public float ParryPercent
        {
            get
            {
                if (m_parry_percent < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_parry_percent;
            }
        }
        private int m_parry_rating = -1;
        public Int32 ParryRating
        {
            get
            {
                if (m_parry_rating < 0 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_parry_rating;
            }
        }

        // <block />
        private float m_block_increase_percent = -1.0F;
        public float BlockIncreasePercent
        {
            get
            {
                if (m_block_increase_percent < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_block_increase_percent;
            }
        }
        private float m_block_percent = -1.0F;
        public float BlockPercent
        {
            get
            {
                if (m_block_percent < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_block_percent;
            }
        }
        private int m_block_rating = -1;
        public Int32 BlockRating
        {
            get
            {
                if (m_block_rating < 0 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_block_rating;
            }
        }

        // <resilience />
        private float m_resilience_damage_percent = -1.0F;
        public float DamageReductionResilience
        {
            get
            {
                if (m_resilience_damage_percent < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_resilience_damage_percent;
            }
        }
        private float m_resilience_hit_percent = -1.0F;
        public float HitReductionResilience
        {
            get
            {
                if (m_resilience_hit_percent < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_resilience_hit_percent;
            }
        }
        private float m_resilience_value = -1.0F;
        public float ResilienceValue
        {
            get
            {
                if (m_resilience_value < 0.0F && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
                }
                return m_resilience_value;
            }
        }
        #endregion

        #region Base Stats 
        // <strength />
        private int m_strength_attack_power_increase = -1;
        public Int32 AttackPowerIncrease
        {
            get
            {
                if (m_strength_base == -1 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetBaseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_BASE_NODE));
                }
                return m_strength_attack_power_increase;
            }
        }
        private int m_strength_base = -1;
        public Int32 StrengthBase
        {
            get
            {
                if (m_strength_base == -1 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetBaseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_BASE_NODE));
                }
                return m_strength_base;
            }
        }
        private int m_strength_block = -1;
        public Int32 BlockIncrease
        {
            get
            {
                if (m_strength_base == -1 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetBaseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_BASE_NODE));
                }
                return m_strength_block;
            }
        }
        private int m_strength_effective = -1;
        public Int32 EffectiveStrength
        {
            get
            {
                if (m_strength_base == -1 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetBaseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_BASE_NODE));
                }
                return m_strength_effective;
            }
        }

        // <agility />
        private int m_agility_armor_increase = -1;
        public Int32 ArmorIncreaseAgility
        {
            get
            {
                if (m_agility_armor_increase == -1 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetBaseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_BASE_NODE));
                }
                return m_agility_armor_increase;
            }
        }
        private int m_agility_attack_power_increase = -1;
        public Int32 AttackPowerIncreaseAgility
        {
            get
            {
                if (m_agility_armor_increase == -1 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetBaseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_BASE_NODE));
                }
                return m_agility_attack_power_increase;
            }
        }
        private int m_agility_base = -1;
        public Int32 BaseAgility
        {
            get
            {
                if (m_agility_armor_increase == -1 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetBaseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_BASE_NODE));
                }
                return m_agility_base;
            }
        }
        private float m_agility_crit_increase_percent = -1.0F;
        public float CritIncreaseAgility
        {
            get
            {
                if (m_agility_armor_increase == -1 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetBaseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_BASE_NODE));
                }
                return m_agility_crit_increase_percent;
            }
        }
        private int m_agility_effective = -1;
        public Int32 EffectiveAgility
        {
            get
            {
                if (m_agility_armor_increase == -1 && XmlData.Length > 0)
                {
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(XmlData);
                    _SetBaseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_BASE_NODE));
                }
                return m_agility_effective;
            }
        }

        // <stamina />
        // <intellect />
        // <spirit />
        #endregion

        #region Network- & data-properties
        /// <summary>
        /// The filename with paramters to call on the webserver. 
        /// Read-only based on CharacterName and RealmName properties.
        /// </summary>
        public String FileName
        {
            get
            {
                if (Character._Loaded)
                    return String.Format("character-sheet.xml?{0}", Character.CharUrl);
                else
                    return String.Format("character-sheet.xml?r={0}&n={1}", Character.RealmName, Character.CharacterName);
            }
        }

        /// <summary>
        /// Holds our xml data so we dont have to load the document several times from the network
        /// </summary>
        private String XmlData
        {
            get;
            set;
        }
        #endregion
        #endregion

        #region Methods
        private void LoadData()
        {
            XmlData = Character.Provider.GetFile(BuildUri(FileName));
            if (!Character._Loaded)
            {
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.LoadXml(XmlData);
                XmlNode characterInfo = xmlDoc.SelectSingleNode(XPATH_CHARACTER_BASIC_INFO_NODE);
                Character._SetProperties(characterInfo);
            }
        }



        #region Internal helper methods
        private void _SetKnownTitles(XmlNodeList list, String title)
        {
            m_currentTitle = title;
            m_knownTitles = new List<string>();
            if (list.Count > 0)
            {
                foreach (XmlNode node in list)
                {
                    m_knownTitles.Add(node.Attributes["value"].Value);
                }
            }
        }
        private void _SetProfessions(XmlNodeList list)
        {
            if (list.Count > 0)
            {
                m_profession1 = new Skill();
                m_profession1.Type = SkillType.Profession;
                m_profession1.Name = list[0].Attributes["name"].Value;
                m_profession1.Current = Int32.Parse(list[0].Attributes["value"].Value);
                m_profession1.Max = Int32.Parse(list[0].Attributes["max"].Value);

                if (list.Count > 1)
                {
                    m_profession2 = new Skill();
                    m_profession2.Type = SkillType.Profession;
                    m_profession2.Name = list[1].Attributes["name"].Value;
                    m_profession2.Current = Int32.Parse(list[1].Attributes["value"].Value);
                    m_profession2.Max = Int32.Parse(list[1].Attributes["max"].Value);
                }
            }
            else
            {
                throw new ArgumentException("No professions found for the given character");
            }
        }
        private void _SetBaseStats(XmlNode node)
        {
            //TODO: load base stats
            // <strength />
            XmlNode strength = node.SelectSingleNode("./strength");
            m_strength_attack_power_increase = Int32.Parse(strength.Attributes["attack"].Value);
            m_strength_attack_power_increase = (m_strength_attack_power_increase == -1 ? 0 : m_strength_attack_power_increase);
            m_strength_base = Int32.Parse(strength.Attributes["base"].Value);
            m_strength_base = (m_strength_base == -1 ? 0 : m_strength_base);
            m_strength_block = Int32.Parse(strength.Attributes["block"].Value);
            m_strength_block = (m_strength_block == -1 ? 0 : m_strength_block);
            m_strength_effective = Int32.Parse(strength.Attributes["effective"].Value);
            m_strength_effective = (m_strength_effective == -1 ? 0 : m_strength_effective);

            // <agility />
            XmlNode agility = node.SelectSingleNode("./agility");
            m_agility_armor_increase = Int32.Parse(agility.Attributes["armor"].Value);
            m_agility_armor_increase = (m_agility_armor_increase == -1 ? 0 : m_agility_armor_increase);
            m_agility_attack_power_increase = Int32.Parse(agility.Attributes["attack"].Value);
            m_agility_attack_power_increase = (m_agility_attack_power_increase == -1 ? 0 : m_agility_attack_power_increase);
            m_agility_base = Int32.Parse(agility.Attributes["base"].Value);
            m_agility_base = (m_agility_base == -1 ? 0 : m_agility_base);
            m_agility_effective = Int32.Parse(agility.Attributes["effective"].Value);
            m_agility_effective = (m_agility_effective == -1 ? 0 : m_agility_effective);
            m_agility_crit_increase_percent = float.Parse(agility.Attributes["critHitPercent"].Value);
            m_agility_crit_increase_percent = (m_agility_crit_increase_percent == -1.0 ? 0.0F : m_agility_crit_increase_percent);

            // <stamina />
            XmlNode stamina = node.SelectSingleNode("./stamina");

            // <intellect />
            XmlNode intellect = node.SelectSingleNode("./intellect");

            // <spirit />
            XmlNode spirit = node.SelectSingleNode("./spirit");

            // <armor /> (cheat) - also loads other defensive stats
            if (m_armor_base == -1)
            {
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.LoadXml(XmlData);
                _SetDefenseStats(xmlDoc.SelectSingleNode(XPATH_CHARACTER_DEFENSE_NODE));
            }
        }
        private void _SetMeleeStats(XmlNode node)
        {
            //TODO: load melee stats
        }
        private void _SetRangedStats(XmlNode node)
        {
            //TODO: load ranged stats
        }
        private void _SetSpellStats(XmlNode node)
        {
            //spell damage bonus
            XmlNode spellDamage = node.SelectSingleNode("./bonusDamage");
            m_arcaneBonus = Int32.Parse(spellDamage.SelectSingleNode("./arcane").Attributes["value"].Value);
            m_arcaneBonus = (m_arcaneBonus < 0 ? 0 : m_arcaneBonus); //set the value to "0" if it's -1; this is done to prevent multiple attempts at loading values
            m_fireBonus = Int32.Parse(spellDamage.SelectSingleNode("./fire").Attributes["value"].Value);
            m_fireBonus = (m_fireBonus < 0 ? 0 : m_fireBonus);
            m_frostBonus = Int32.Parse(spellDamage.SelectSingleNode("./frost").Attributes["value"].Value);
            m_frostBonus = (m_frostBonus < 0 ? 0 : m_frostBonus);
            m_holyBonus = Int32.Parse(spellDamage.SelectSingleNode("./holy").Attributes["value"].Value);
            m_holyBonus = (m_holyBonus < 0 ? 0 : m_holyBonus);
            m_natureBonus = Int32.Parse(spellDamage.SelectSingleNode("./nature").Attributes["value"].Value);
            m_natureBonus = (m_natureBonus < 0 ? 0 : m_natureBonus);
            m_shadowBonus = Int32.Parse(spellDamage.SelectSingleNode("./shadow").Attributes["value"].Value);
            m_shadowBonus = (m_shadowBonus < 0 ? 0 : m_shadowBonus);

            //healing bonus
            XmlNode healingBonus = node.SelectSingleNode("./bonusHealing");
            m_healingBonus = Int32.Parse(healingBonus.Attributes["value"].Value);

            ////hit stuff
            XmlNode hitRatingNode = node.SelectSingleNode("./hitRating");
            m_increasedSpellHitPercent = float.Parse(hitRatingNode.Attributes["increasedHitPercent"].Value);
            m_increasedSpellHitPercent = (m_increasedSpellHitPercent == -1.0F ? 0F : m_increasedSpellHitPercent);
            m_penetration = Int32.Parse(hitRatingNode.Attributes["penetration"].Value);
            m_penetration = (m_penetration == -1 ? 0 : m_penetration);
            m_reducedResist = Int32.Parse(hitRatingNode.Attributes["reducedResist"].Value);
            m_reducedResist = (m_reducedResist == -1 ? 0 : m_reducedResist);
            m_spellHitratingValue = Int32.Parse(hitRatingNode.Attributes["value"].Value);
            m_spellHitratingValue = (m_spellHitratingValue == -1 ? 0 : m_spellHitratingValue);

            //crit chance
            XmlNode critChanceNode = node.SelectSingleNode("./critChance");
            m_spellCritRating = Int32.Parse(critChanceNode.Attributes["rating"].Value);
            m_spellCritRating = (m_spellCritRating == -1 ? 0 : m_spellCritRating);
            m_arcaneCritPercent = float.Parse(critChanceNode.SelectSingleNode("./arcane").Attributes["percent"].Value);
            m_arcaneCritPercent = (m_arcaneCritPercent == -1F ? 0F : m_arcaneCritPercent);
            m_fireCritPercent = float.Parse(critChanceNode.SelectSingleNode("./fire").Attributes["percent"].Value);
            m_fireCritPercent = (m_fireCritPercent == -1F ? 0F : m_fireCritPercent);
            m_frostCritPercent = float.Parse(critChanceNode.SelectSingleNode("./frost").Attributes["percent"].Value);
            m_frostCritPercent = (m_frostCritPercent == -1F ? 0F : m_frostCritPercent);
            m_holyCritPercent = float.Parse(critChanceNode.SelectSingleNode("./holy").Attributes["percent"].Value);
            m_holyCritPercent = (m_holyCritPercent == -1F ? 0F : m_holyCritPercent);
            m_natureCritPercent = float.Parse(critChanceNode.SelectSingleNode("./nature").Attributes["percent"].Value);
            m_natureCritPercent = (m_natureCritPercent == -1F ? 0F : m_natureCritPercent);
            m_shadowCritPercent = float.Parse(critChanceNode.SelectSingleNode("./shadow").Attributes["percent"].Value);
            m_shadowCritPercent = (m_shadowCritPercent == -1F ? 0F : m_shadowCritPercent);

            //spell penetration
            m_spellPenetration = Int32.Parse(node.SelectSingleNode("./penetration").Attributes["value"].Value);
            m_spellPenetration = (m_spellPenetration == -1 ? 0 : m_spellPenetration);

            //mana regen
            m_manaRegenCasting = float.Parse(node.SelectSingleNode("./manaRegen").Attributes["casting"].Value);
            m_manaRegenCasting = (m_manaRegenCasting == -1F ? 0F : m_manaRegenCasting);
            m_manaRegenNotCasting = float.Parse(node.SelectSingleNode("./manaRegen").Attributes["notCasting"].Value);
            m_manaRegenNotCasting = (m_manaRegenNotCasting == -1F ? 0F : m_manaRegenNotCasting);

            //spell haste
            m_spellHasteRating = Int32.Parse(node.SelectSingleNode("./hasteRating").Attributes["hasteRating"].Value);
            m_spellHasteRating = (m_spellHasteRating == -1 ? 0 : m_spellHasteRating);
            m_spellHastePercent = float.Parse(node.SelectSingleNode("./hasteRating").Attributes["hastePercent"].Value);
            m_spellHastePercent = (m_spellHastePercent == -1F ? 0F : m_spellHastePercent);
        }
        private void _SetDefenseStats(XmlNode node)
        {
            // <armor />
            m_armor_base = Int32.Parse(node.SelectSingleNode("./armor").Attributes["base"].Value);
            m_armor_base = (m_armor_base == -1 ? 0 : m_armor_base); // check made to prevent loading multiple times
            m_armor_effective = Int32.Parse(node.SelectSingleNode("./armor").Attributes["effective"].Value);
            m_armor_effective = (m_armor_effective == -1 ? 0 : m_armor_effective);
            m_armor_percent = float.Parse(node.SelectSingleNode("./armor").Attributes["percent"].Value);
            m_armor_percent = (m_armor_percent == -1.0F ? 0.0F : m_armor_percent);
            m_armor_pet = Int32.Parse(node.SelectSingleNode("./armor").Attributes["petBonus"].Value);
            m_armor_pet = (m_armor_pet == -1 ? 0 : m_armor_pet);

            // <defense />
            m_defense_decrease_percent = float.Parse(node.SelectSingleNode("./defense").Attributes["decreasePercent"].Value);
            m_defense_decrease_percent = (m_defense_decrease_percent == -1.0F ? 0.0F : m_defense_decrease_percent);
            m_defense_increase_percent = float.Parse(node.SelectSingleNode("./defense").Attributes["increasePercent"].Value);
            m_defense_increase_percent = (m_defense_increase_percent == -1.0F ? 0.0F : m_defense_increase_percent);
            m_defense_additional_defense = Int32.Parse(node.SelectSingleNode("./defense").Attributes["plusDefense"].Value);
            m_defense_additional_defense = (m_defense_additional_defense == -1 ? 0 : m_defense_additional_defense);
            m_defense_rating = Int32.Parse(node.SelectSingleNode("./defense").Attributes["rating"].Value);
            m_defense_rating = (m_defense_rating == -1 ? 0 : m_defense_rating);
            m_defense_value = float.Parse(node.SelectSingleNode("./defense").Attributes["value"].Value);
            m_defense_value = (m_defense_value == -1.0F ? 0.0F : m_defense_value);

            // <dodge />
            m_dodge_increase_percent = float.Parse(node.SelectSingleNode("./dodge").Attributes["increasePercent"].Value);
            m_dodge_increase_percent = (m_dodge_increase_percent == -1.0F ? 0.0F : m_dodge_increase_percent);
            m_dodge_percent = float.Parse(node.SelectSingleNode("./dodge").Attributes["percent"].Value);
            m_dodge_percent = (m_dodge_percent == -1.0F ? 0.0F : m_dodge_percent);
            m_dodge_rating = Int32.Parse(node.SelectSingleNode("./dodge").Attributes["rating"].Value);
            m_dodge_rating = (m_dodge_rating == -1 ? 0 : m_dodge_rating);

            // <parry />
            m_parry_increase_percent = float.Parse(node.SelectSingleNode("./parry").Attributes["increasePercent"].Value);
            m_parry_increase_percent = (m_parry_increase_percent == -1.0F ? 0.0F : m_parry_increase_percent);
            m_parry_percent = float.Parse(node.SelectSingleNode("./parry").Attributes["percent"].Value);
            m_parry_percent = (m_parry_percent == -1.0F ? 0.0F : m_parry_percent);
            m_parry_rating = Int32.Parse(node.SelectSingleNode("./parry").Attributes["rating"].Value);
            m_parry_rating = (m_parry_rating == -1 ? 0 : m_parry_rating);

            // <block />
            m_block_increase_percent = float.Parse(node.SelectSingleNode("./block").Attributes["increasePercent"].Value);
            m_block_increase_percent = (m_block_increase_percent == -1.0F ? 0.0F : m_block_increase_percent);
            m_block_percent = float.Parse(node.SelectSingleNode("./block").Attributes["percent"].Value);
            m_block_percent = (m_block_percent == -1.0F ? 0.0F : m_block_percent);
            m_block_rating = Int32.Parse(node.SelectSingleNode("./block").Attributes["rating"].Value);
            m_block_rating = (m_block_rating == -1 ? 0 : m_block_rating);

            // <resilience />
            m_resilience_damage_percent = float.Parse(node.SelectSingleNode("./resilience").Attributes["damagePercent"].Value);
            m_resilience_damage_percent = (m_resilience_damage_percent == -1.0F ? 0.0F : m_resilience_damage_percent);
            m_resilience_hit_percent = float.Parse(node.SelectSingleNode("./resilience").Attributes["hitPercent"].Value);
            m_resilience_hit_percent = (m_resilience_hit_percent == -1.0F ? 0.0F : m_resilience_hit_percent);
            m_resilience_value = float.Parse(node.SelectSingleNode("./resilience").Attributes["value"].Value);
            m_resilience_value = (m_resilience_value == -1.0F ? 0.0F : m_resilience_value);
        }
        private void _SetResistances(XmlDocument xmlDoc)
        {
            m_fireResistance = new FireResistance();
            m_fireResistance.PetBonus = Int32.Parse(xmlDoc.SelectSingleNode(XPATH_CHARACTER_RESISTANCES_NODE + "/fire").Attributes["petBonus"].Value);
            m_fireResistance.Value = Int32.Parse(xmlDoc.SelectSingleNode(XPATH_CHARACTER_RESISTANCES_NODE + "/fire").Attributes["value"].Value);

            m_frostResistance = new FrostResistance();
            m_frostResistance.PetBonus = Int32.Parse(xmlDoc.SelectSingleNode(XPATH_CHARACTER_RESISTANCES_NODE + "/frost").Attributes["petBonus"].Value);
            m_frostResistance.Value = Int32.Parse(xmlDoc.SelectSingleNode(XPATH_CHARACTER_RESISTANCES_NODE + "/frost").Attributes["value"].Value);

            m_shadowResistance = new ShadowResistance();
            m_shadowResistance.PetBonus = Int32.Parse(xmlDoc.SelectSingleNode(XPATH_CHARACTER_RESISTANCES_NODE + "/shadow").Attributes["petBonus"].Value);
            m_shadowResistance.Value = Int32.Parse(xmlDoc.SelectSingleNode(XPATH_CHARACTER_RESISTANCES_NODE + "/shadow").Attributes["value"].Value);

            m_natureResistance = new NatureResistance();
            m_natureResistance.PetBonus = Int32.Parse(xmlDoc.SelectSingleNode(XPATH_CHARACTER_RESISTANCES_NODE + "/nature").Attributes["petBonus"].Value);
            m_natureResistance.Value = Int32.Parse(xmlDoc.SelectSingleNode(XPATH_CHARACTER_RESISTANCES_NODE + "/nature").Attributes["value"].Value);

            m_arcaneResistance = new ArcaneResistance();
            m_arcaneResistance.PetBonus = Int32.Parse(xmlDoc.SelectSingleNode(XPATH_CHARACTER_RESISTANCES_NODE + "/arcane").Attributes["petBonus"].Value);
            m_arcaneResistance.Value = Int32.Parse(xmlDoc.SelectSingleNode(XPATH_CHARACTER_RESISTANCES_NODE + "/arcane").Attributes["value"].Value);

            m_holyResistance = new HolyResistance();
            m_holyResistance.PetBonus = Int32.Parse(xmlDoc.SelectSingleNode(XPATH_CHARACTER_RESISTANCES_NODE + "/holy").Attributes["petBonus"].Value);
            m_holyResistance.Value = Int32.Parse(xmlDoc.SelectSingleNode(XPATH_CHARACTER_RESISTANCES_NODE + "/holy").Attributes["value"].Value);
        }
        private void _SetCharacterBars(XmlNode node)
        {
            m_healthBar = new HealthBar();
            m_healthBar.Effective = Int32.Parse(node.SelectSingleNode("./health").Attributes["effective"].Value);
            XmlNode resourceNode = node.SelectSingleNode("./secondBar");
            int casting = 0, effective = 0, notCasting = 0;
            string resourceType = resourceNode.Attributes["type"].Value;
            casting = Int32.Parse(resourceNode.Attributes["casting"].Value);
            notCasting = Int32.Parse(resourceNode.Attributes["notCasting"].Value);
            effective = Int32.Parse(resourceNode.Attributes["effective"].Value);
            if (resourceType.Equals("m"))
            {
                m_resourceBar = new ManaBar();
                ((ManaBar)m_resourceBar).RegenCasting = casting;
                ((ManaBar)m_resourceBar).RegenNotCasting = notCasting;
            }
            else if (resourceType.Equals("e")) {
                m_resourceBar = new EnergyBar();
            }
            else if (resourceType.Equals("p"))
            {
                m_resourceBar = new RunicBar();
            }
            else if (resourceType.Equals("r"))
            {
                m_resourceBar = new RageBar();
            }
            else
                throw new ArgumentException();
            m_resourceBar.Effective = effective;
        }
        public override void Load()
        {
            LoadData();
        }
        #endregion
        #endregion
    }

    #region Resistances
    public abstract class Resistance
    {
        public Int32 Value
        {
            get;
            set;
        }
        private Int32 m_petBonus = -1;
        public Int32 PetBonus
        {
            get
            {
                return (m_petBonus == -1 ? 0 : m_petBonus);
            }
            set
            {
                m_petBonus = value;
            }
        }
    }
    public class FireResistance : Resistance
    {
    }
    public class FrostResistance : Resistance
    {
    }
    public class ShadowResistance : Resistance
    {
    }
    public class NatureResistance : Resistance
    {
    }
    public class ArcaneResistance : Resistance
    {
    }
    public class HolyResistance : Resistance
    {
    }
    #endregion

    #region Character Bars
    public abstract class CharacterBar {
        public Int32 Effective {
            get;
            internal set;
        }
    }
    public class HealthBar : CharacterBar {
    }
    public class ManaBar : CharacterBar {
        public Int32 RegenNotCasting {
            get;
            internal set;
        }
        public Int32 RegenCasting {
            get;
            internal set;
        }
    }
    public class EnergyBar : CharacterBar {
    }
    public class RunicBar : CharacterBar {
    }
    public class RageBar : CharacterBar
    {
    }
    #endregion
}